package controller

import (
	"encoding/json"
	"errors"
	"gin/response"
	"gin/service"
	"gin/validate"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	teamones_helper "github.com/teamones-open/go-helper"
	"mime/multipart"
	"os"
	"strings"
	"time"
)

var (
	uploadFileKey     = "Filedata"
	PackageType       = []interface{}{"client_win", "client_linux", "client_mac", "app"}
	ClientWinFileType = []interface{}{"exe", "yml"}
	AppFileType       = []interface{}{"apk"}
)

func UploadFile(ctx *gin.Context) {

	var err error

	_, err = ctx.FormFile(uploadFileKey)
	packageType := ctx.PostForm("type")
	packageVersion := ctx.PostForm("version")

	if len(packageType) == 0 || !teamones_helper.InArray(packageType, PackageType) {
		err = errors.New("Package type parameter must exist.")
	}

	if len(packageVersion) == 0 {
		err = errors.New("Version parameter must exist.")
	}

	if err != nil {
		//ignore
		response.Fail(ctx, nil, err.Error())
		return
	} else {
		fileMetadataItems := make([]validate.FileMetadata, 0)
		var fileMetadata validate.FileMetadata

		for _, file := range ctx.Request.MultipartForm.File["Filedata"] {

			switch packageType {
			case "client_win":
				fileMetadata, err = DealFileItem(ctx, file, ClientWinFileType, packageType, packageVersion)
				break
			case "app":
				fileMetadata, err = DealFileItem(ctx, file, AppFileType, packageType, packageVersion)
				break
			default:
				err = errors.New("Illegal operation.")
			}

			if err == nil {
				fileMetadataItems = append(fileMetadataItems, fileMetadata)
			}
		}

		if err != nil {
			response.Fail(ctx, nil, err.Error())
		} else {
			response.Success(ctx, gin.H{"data": fileMetadataItems}, "Upload successful")
		}
	}
}

// 处理单个文件数据
func DealFileItem(ctx *gin.Context, File *multipart.FileHeader, FileType []interface{}, packageType string, packageVersion string) (fileMetadata validate.FileMetadata, err error) {

	// 文件后缀名
	fileExt := teamones_helper.GetFileExt(File.Filename)

	if teamones_helper.InArray(fileExt, FileType) {

		tempPath, folderName, MoveErr := MoveFileToTempFolder(ctx, File, fileExt)

		if MoveErr != nil {
			err = MoveErr
			return
		}

		// 获取当前文件的md5值
		fileMd5 := teamones_helper.GetFileMd5(tempPath)

		// 判断当前md5文件是否存在
		checkResult, fileDBData := service.CheckFileMd5(fileMd5)

		if checkResult {
			// 存在直接返回
			jsonStr, _ := json.Marshal(fileDBData.Param)
			_ = json.Unmarshal(jsonStr, &fileMetadata)

			// 存在之间删除临时文件
			err = os.Remove(tempPath)
		} else {
			// 遍历赋值
			fileMetadata.Md5 = fileMd5
			fileMetadata.Name = File.Filename
			fileMetadata.Type = packageType
			fileMetadata.Ext = fileExt
			fileMetadata.FileSize = File.Size
			fileMetadata.Version = packageVersion

			addFile := validate.AddFile{}

			switch packageType {
			case "client_win", "client_linux", "client_mac":
				// 处理客户端更新包文件
				addFile.Path = viper.GetString("server.staticPath") + "package/client/" + strings.Replace(packageType, "client_", "", -1) + "/" + File.Filename
				fileMetadata.Path = addFile.Path
				err = DealClientPackage(tempPath, addFile.Path)
				break
			default:
				err = errors.New("Illegal operation.")
				return
			}

			if err == nil {
				// 保存文件数据
				addFile.Name = folderName
				addFile.Md5 = fileMd5
				addFile.Type = packageType
				addFile.Version = packageVersion
				addFile.Param, _ = json.Marshal(fileMetadata)
				err = service.CreateFile(addFile)
			}
		}
	} else {
		err = errors.New("The update package type only support exe and yaml.")
	}

	return
}

// 移动文件到临时文件夹
func MoveFileToTempFolder(ctx *gin.Context, File *multipart.FileHeader, fileExt string) (tempPath string, folderName string, err error) {

	// 文件名称
	folderName = teamones_helper.RandStringBytesMaskImprSrcUnsafe(10) + "_" + teamones_helper.GenerateMd5(time.Now().String())

	// 临时文件名
	dstName := folderName + "." + fileExt

	// 临时存放地址
	tempPath = viper.GetString("server.staticPath") + "temp/" + dstName

	// 拷贝到对应文件夹
	err = ctx.SaveUploadedFile(File, tempPath)

	return
}

// 处理package文件
func DealClientPackage(tempPath string, filePath string) (err error) {
	err = os.Rename(tempPath, filePath)
	return
}

// 通过 Name 查找文件
func SelectFileByName(ctx *gin.Context) {
	var fileMd5sFilter validate.FileMd5sFilter
	err := ctx.ShouldBindJSON(&fileMd5sFilter)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	fileData, err := service.SelectFileByName(fileMd5sFilter.Name)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": fileData}, "Select successfully.")
}

// 通过 Name 删除文件
func RemoveFileByName(ctx *gin.Context) {
	var fileMd5sFilter validate.FileMd5sFilter
	err := ctx.ShouldBindJSON(&fileMd5sFilter)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	err = service.RemoveFileByName(fileMd5sFilter.Name)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{}, "Delete successfully.")
}
